package com.gjs.freechat.server.business;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

import javax.imageio.ImageIO;

import org.apache.commons.lang.math.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.gjs.freechat.server.component.ChatSessionManage;
import com.gjs.freechat.server.component.UserManage;
import com.gjs.freechat.server.component.UserMatchManage;
import com.gjs.freechat.server.component.UserSessionPool;
import com.gjs.freechat.server.component.UsersPoolManage;
import com.gjs.freechat.server.component.bean.ChatSession;
import com.gjs.freechat.server.component.bean.Conversation;
import com.gjs.freechat.server.component.bean.Message;
import com.gjs.freechat.server.component.bean.UserInfo;
import com.gjs.freechat.utils.DateUtil;
import com.gjs.freechat.utils.FileUtils;
import com.gjs.freechat.utils.ImageUtils;
import com.gjs.freechat.web.business.WebUserConnectionPool;

@Service
public class ChatSessionBusinessImpl implements ChatSessionBusiness {
	
	
	@Autowired
	private WebUserConnectionPool connectionPool;
	
	@Autowired
	private UserSessionPool sessionPool;
	
	@Autowired
	private UsersPoolManage usersPool;
	
	@Autowired
	private ChatSessionManage chatSessionManage;
	
	@Autowired
	private UserMatchManage userMatchManage;
	
	@Autowired
	private UserManage userManage;
	
	@Autowired
	private ThreadPoolTaskExecutor poolTaskExecutor;
	
	@Override
	public void userLoginStatusMessagePush(String userId, int status) {
		Message msg = new Message();
		msg.setSenderId(userId);
		msg.setMsgType(Message.MESSAGE_TYPE_LOGIN_STATUS);
		msg.setContent("" + status);
		List<Conversation> convos = sessionPool.getExistedConvos(userId);
		connectionPool.sendMessages(msg, convos);
	}
	@Override
	public void onlineUsersCountMsgPush() {
		List<String> userIds = userMatchManage.getMatchingUserIds();
		Message msg = new Message();
		msg.setMsgType(Message.MESSAGE_TYPE_ONLINE_USER_NUM);
		msg.setContent("" + usersPool.getOnlineUsersCount());
		for (String id : userIds) {
			msg.setReceiverId(id);
			connectionPool.sendMessage(msg);
		}
	}
	
	public void userMatchSuccessMsgPush(Conversation convo, String oldSessionId) {
		Message msg = new Message();
		msg.setMsgType(Message.MESSAGE_TYPE_USER_MATCH);
		msg.setReceiverId(convo.getUserId());
		msg.setSessionId(oldSessionId);
		msg.setContent(JSON.toJSONString(convo));
		connectionPool.sendMessage(msg);
	}
	
	public void removeConvoMsgPush(String convoId, String receiverId) {
		Message msg = new Message();
		msg.setMsgType(Message.MESSAGE_TYPE_REMOVE_CONVO);
		msg.setContent(convoId);
		msg.setReceiverId(receiverId);
		connectionPool.sendMessage(msg);
	}
	
	@Override
	public String searchAddByUsername(String userId, String username) {
		UserInfo partner = userManage.getUserInfoByUserName(username);
		Map<String, Object> res = new HashMap<String, Object>();
		if (partner != null) {
			UserInfo userInfo = usersPool.get(userId);
			Map<String, Object> sessionMap = addNewSession(userInfo, partner);
			if (sessionMap != null) {
				Conversation userConvo = (Conversation) sessionMap.get("userConvo");
				Conversation partnerConvo = (Conversation) sessionMap.get("partnerConvo");
				userMatchSuccessMsgPush(partnerConvo, partnerConvo.getId());
				res.put("resultCode", 2);    //搜索到用户
				res.put("data", userConvo);
			} else {
				 ChatSession chatSession = sessionPool.getSession(userId, partner.getId());
				 res.put("resultCode", 1);    //已经建立了会话
				 res.put("data", chatSession.getId());
			}
			
		} else {
			res.put("resultCode", 0);    //没有搜索到
		}
		return JSON.toJSONString(res);
	}
	
	@Override
	public Map<String, Object> addNewSession(UserInfo userInfo, UserInfo partner) {
		String sessionId = UUID.randomUUID().toString();
		ChatSession chatSession = new ChatSession(sessionId, Conversation.CONVO_TYPE_NORMAL, userInfo.getId(), partner.getId());
		if (sessionPool.addSession(chatSession)) {
			Map<String, Object> sessionMap = chatSessionManage.createNewChatSession(sessionId, 
					Conversation.CONVO_TYPE_NORMAL, userInfo, partner);
			Conversation userConvo = (Conversation) sessionMap.get("userConvo");
			Conversation partnerConvo = (Conversation) sessionMap.get("partnerConvo");
			sessionPool.addConvo(userConvo);
			if (partner.getLogined() == 1)
				sessionPool.addConvo(partnerConvo);
			return sessionMap;
		}
		return null;
	}
	
	@Override
	public void addNewConvo(Conversation convo) {
		sessionPool.addConvo(convo);
		chatSessionManage.addNewConvo(convo);
		if (convo.getType() == Conversation.CONVO_TYPE_WAITING) {
			startMatch(convo);
			onlineUsersCountMsgPush();
		}
	}
	
	@Override
	public void removeConvo(String convoId, String userId, String partnerId) {
		chatSessionManage.removeSession(convoId);
		sessionPool.removeSession(new ChatSession(null, -1, userId, partnerId));
		sessionPool.removeConvo(userId, convoId);
		sessionPool.removeConvo(partnerId, convoId);
		removeConvoMsgPush(convoId, partnerId);
	}
	
	@Override
	public void addFriendRequest(String convoId, String userId, String partnerId) {
		Message msg = new Message(convoId, Message.MESSAGE_TYPE_ADD_FRIEND_REQUEST, "", userId, partnerId);
		msg.setSendTime(DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm"));
		chatSessionManage.addFriendRequest(convoId, userId);
		sendMessage(msg);
	}
	
	@Override
	public void rejectFriend(String convoId, String userId, String partnerId) {
		Message msg = new Message(convoId, Message.MESSAGE_TYPE_ADD_FRIEND_REQUEST_STATUS, "0", userId, partnerId);
		chatSessionManage.rejectFriendRequest(convoId);
		connectionPool.sendMessage(msg);
	}
	
	@Override
	public void acceptFriend(String convoId, String userId, String partnerId) {
		Message msg = new Message(convoId, Message.MESSAGE_TYPE_ADD_FRIEND_REQUEST_STATUS, "1", userId, partnerId);
		chatSessionManage.acceptFriendRequest(convoId);
		connectionPool.sendMessage(msg);
	}
	
	@Override
	public void updateFriendNickName(String convoId, String userId, String name) {
		chatSessionManage.updateFriendNickName(convoId, userId, name);
	}
	
	@Override
	public void sendMessage(Message msg) {
		if (!connectionPool.sendMessage(msg)) {
			chatSessionManage.addUnreadMessage(msg.getSessionId(), msg.getReceiverId());
		}
	}
	
	@Override
	public void sendTextMessage(Message msg) {
		chatSessionManage.saveMessage(msg);
		sendMessage(msg);
	}
	
	@Override
	public Message sendImage(BufferedImage bufferedImage, Message msg,
			int[] imgCoords) {
		//System.out.println(Arrays.toString(imgCoords));
		// BufferedImage img = ImageIO.read(new File(realImgPath));
		bufferedImage = ImageUtils.sizeAdjust(bufferedImage, imgCoords);
		String imgName = msg.getContent();
		String imgFilePath = ChatSessionManage.IMAGE_STORE_LOCATION + imgName;
		File imgFile = FileUtils.createNewFile(imgFilePath);
		try {
			ImageIO.write(bufferedImage, imgName.substring(imgName.indexOf('.') + 1), imgFile);
			System.out.println(imgName);
			msg.setContent(imgName);
			chatSessionManage.saveMessage(msg);
			sendMessage(msg);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return msg;
	}
	
	@Override
	public void startMatch(Conversation convo) {
		convo.setMatching(true);
		UserInfo userInfo = usersPool.get(convo.getUserId());
		userMatchManage.addConvo(userInfo, convo);
		poolTaskExecutor.execute(new MatchTask(userInfo, convo));
	}
	
	@Override
	public void startMatch(String userId, String sessionId) {
		Conversation waitingConvo = sessionPool.getConvo(userId, sessionId);
		if (waitingConvo == null)
			return;
		startMatch(waitingConvo);
		onlineUsersCountMsgPush();
	}
	
	@Override
	public void stopMatch(String userId, String convoId) {
		//System.out.println("entry:" + userId + "," + convoId);
		Conversation waitingConvo = userMatchManage.getConvo(userId, convoId);
		if (waitingConvo == null)
			return;
		//System.out.println(userId + "," + convoId);
		waitingConvo.setMatching(false);
		UserInfo userInfo = new UserInfo();
		userInfo.setId(userId);
		userMatchManage.removeConvo(userInfo, waitingConvo);
		sessionPool.removeConvo(userId, convoId);
		chatSessionManage.removeConvo(userId, convoId);
	}
	
	@Override
	public void stopAllMatch(String userId) {
		List<Conversation> waitingConvos = userMatchManage.getConvos(userId);
		if (waitingConvos == null)
			return;
		for (Conversation convo : waitingConvos)
			convo.setMatching(false);
	}
	
	
	
	class MatchTask implements Runnable {
		
		private Conversation convo;
		private UserInfo userInfo;
		
		public MatchTask(UserInfo userInfo, Conversation convo) {
			this.convo = convo;
			this.userInfo = userInfo;
		}
		
		@Override
		public void run() {
			int count = 20;
			Conversation partnerConvo = null;
			ChatSession chatSession = new ChatSession(convo.getId(), Conversation.CONVO_TYPE_NORMAL, userInfo.getId(), null);
			while (count-- >= 0) {
				synchronized (convo) {
					if (!convo.isMatching() || convo.getType() != Conversation.CONVO_TYPE_WAITING) {
						break;
					}
				}
				UserInfo partner = userMatchManage.executeMatch(userInfo, chatSession);
				if (partner == null) {
					try {
						Thread.sleep(RandomUtils.nextInt(new Random(new Date().getTime()), 2000));
						continue;
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				do {
					partnerConvo = userMatchManage.getRandomConvo(partner.getId());
					if (partnerConvo != null) {
						synchronized (partnerConvo) {
							synchronized (convo) {
								if (partnerConvo.getType() == Conversation.CONVO_TYPE_WAITING &&
										partnerConvo.isMatching() && convo.isMatching() && 
										convo.getType() == Conversation.CONVO_TYPE_WAITING) {
									String createTime = DateUtil.formatDate(new Date());
									partnerConvo.setMatching(false);
									partnerConvo.setType(Conversation.CONVO_TYPE_NORMAL);
									partnerConvo.setPartner(userInfo);
									partnerConvo.setPartnerNickName(userInfo);
									partnerConvo.setCreateTime(createTime);
									userMatchManage.removeConvo(userInfo, partnerConvo);
									String oldSessionId = partnerConvo.getId();
									partnerConvo.setId(convo.getId());
									chatSessionManage.updateConvo(partnerConvo, oldSessionId);									
									userMatchSuccessMsgPush(partnerConvo, oldSessionId);
									
									convo.setMatching(false);
									convo.setType(Conversation.CONVO_TYPE_NORMAL);
									convo.setPartner(partner);
									convo.setPartnerNickName(partner);
									convo.setCreateTime(createTime);
									userMatchManage.removeConvo(userInfo, convo);
									chatSessionManage.updateConvo(convo, convo.getId());
									userMatchSuccessMsgPush(convo, convo.getId());

									chatSessionManage.addNewSession(chatSession);
									return;
								} else if (convo.getType() != Conversation.CONVO_TYPE_WAITING || !convo.isMatching()) {
									break;
								} else {
									partnerConvo = null;
								}
							}
						}
					} else {
						break;
					}
				} while (partnerConvo == null);
				sessionPool.removeSession(chatSession);
				try {
					Thread.sleep(RandomUtils.nextInt(new Random(new Date().getTime()), 2000));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
	}
}
